﻿using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.Linq;
using System.Text;
using Microsoft.Win32;
using System.Security;
using System.Collections;
using System.Reflection;
using System.IO;
using System.Diagnostics;


namespace BTSZap
{
    class Program
    {

        [Conditional("DEBUG")]
        private void Break()
        {
            Break(string.Empty);            
        }

        [Conditional("DEBUG")]
        private void Break(string s)
        {
            ConsoleColor exColor = Console.ForegroundColor;
            Console.ForegroundColor = ConsoleColor.Blue;
            Console.Write("break: " + s + " ...");
            CancelableConfirm();
            Console.WriteLine();
            Console.ForegroundColor = exColor;
        }

        private void CancelableConfirm()
        {
            ConsoleKey key;
            do
            {
                key = Console.ReadKey(true).Key;

            } while ((key != ConsoleKey.Escape) && (key != ConsoleKey.Enter));
            if (key == ConsoleKey.Escape)
            {
                Console.WriteLine(Properties.Resources.msgCanceled);
                Environment.Exit(-2);

            }
        }
        private void Run(string[] args)
        {
            Console.WriteLine("BTSZAP : a utility to remove a BizTalk application");
            Console.WriteLine("Developed by Roch BADUEL");
            Console.WriteLine("web site : www.btug.fr");

            Properties.Resources.Culture = System.Threading.Thread.CurrentThread.CurrentCulture;
            if ((args.Length == 0) || ((args.Length == 1) && ("/?,-?".Contains(args[0]))))
            {
                Console.WriteLine(Arguments.GetArgumentsDescrition());
                Environment.Exit(0);
            }
            Arguments.Init(args);
            if (Arguments.Current.Error) StopOnError(Arguments.Current.ErrorReason);

            Console.WriteLine(Arguments.Current.GetConfigurationDescription());
            if (!Arguments.Current.NoConfirmation)
            {
                Console.WriteLine(Properties.Resources.msgConfirmLaunch);
                CancelableConfirm();
                UnInstall();

                Break();

            }
            else
                UnInstall();
        }



        private void StopOnError(string p)
        {
            Console.ForegroundColor = ConsoleColor.Red;
            Console.WriteLine(p);
            Console.WriteLine();
            
            string exeName = System.Reflection.Assembly.GetExecutingAssembly().GetName().Name;
            Console.ForegroundColor = ConsoleColor.Green;
            Console.WriteLine(string.Format("Syntaxe : {0} Argument1:Value1 Argument2:Value2  ",exeName));
            Console.ResetColor();
            Console.WriteLine();
            Console.WriteLine(Arguments.GetArgumentsDescrition());

            Break();
            if (Arguments.Current.HaltOnError) Environment.Exit(-1);
        }

        private void Error(int code)
        {
            Break(String.Format("Err {0}", code));
            Environment.Exit(code);
        }


        private void AutoDetection()
        {
            /* auto detection
             * We try to read registry in order to detect Administration Plugin default connection
             */
            Log.WriteStartInformation(Properties.Resources.msgTryAutoDetect);
            try
            {
                Arguments.Current.ManagementDatabase = (string)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\BizTalk Server\3.0\Administration", "MgmtDBName", Arguments.Current.ManagementDatabase);
                Arguments.Current.DatabaseServer = (string)Registry.GetValue(@"HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\BizTalk Server\3.0\Administration", "MgmtDBServer", Arguments.Current.DatabaseServer);
                Log.WriteEndSucess(Properties.Resources.msgAutoDetected, Arguments.Current.DatabaseServer, Arguments.Current.ManagementDatabase);
            }
            catch (Exception ex)
            {
                Log.WriteException(ex);
                Log.WriteEndWarning(Properties.Resources.msgErrAutoDetected);

            }
        }

        private void AccessGroup()
        {
            Log.WriteStartInformation(Properties.Resources.msgTryOpenConnection);
            //Create a group object and configure it with connection parameters
            grp = new Microsoft.BizTalk.ApplicationDeployment.Group();
            grp.DBName = Arguments.Current.ManagementDatabase;
            grp.DBServer = Arguments.Current.DatabaseServer;
            //Try to access the Default Application in order to test the connection
            try
            {
                string defaultAppName = grp.CatalogExplorer.DefaultApplication.Name;
                catalogExplorer = grp.CatalogExplorer;
                Log.WriteEndSucess(Properties.Resources.msgConnectionOpened);
            }
            catch (Exception ex)
            {
                Log.WriteException(ex);
                Log.WriteEndError(Properties.Resources.errOpeningConnection);
                Error(-100);
            }

        }




        private Microsoft.BizTalk.ExplorerOM.Application app = null;
        private Microsoft.BizTalk.ExplorerOM.BtsCatalogExplorer catalogExplorer = null;
        private Microsoft.BizTalk.ApplicationDeployment.Group grp = null;
        private Microsoft.BizTalk.ApplicationDeployment.Application grpApp = null;



        private void AccessApp()
        {
            //Try to acccess the application
            Log.WriteStartInformation(Properties.Resources.msgTryAccessApp, Arguments.Current.ApplicationName);       
            //we use ExplorerOM API since there is a Bug in ApplicationDeployment
            //(Bug is : Stop does not work)                                    
            try
            {
                app = catalogExplorer.Applications[Arguments.Current.ApplicationName];
                grpApp = grp.Applications[Arguments.Current.ApplicationName];
            }
            catch (Exception ex)
            {
                Log.WriteException(ex);
                Log.WriteEndError(Properties.Resources.errAppDoesNotExists, Arguments.Current.ApplicationName);
                Error(-101); 
            }
            Log.WriteEndSucess(Properties.Resources.msgAppAccessed,Arguments.Current.ApplicationName);     
        }


        private void StopApp()
        {
            //Try to stop application
            Log.WriteStartInformation(Properties.Resources.msgTryStopApp);
            if (Arguments.Current.FullStop)
                Log.WriteInformation(Properties.Resources.msgFullStop);
            else
                Log.WriteInformation(Properties.Resources.msgSimpleStop);

            //Log.WriteStartInformation(Properties.Resources.msgst);
            try
            {
                app.Stop(Microsoft.BizTalk.ExplorerOM.ApplicationStopOption.StopAll);
                catalogExplorer.SaveChanges();
                Log.WriteEndSucess(Properties.Resources.msgAppStopped);
            }
            catch (Exception ex)
            {
                Log.WriteException(ex);
                Log.WriteEndError(Properties.Resources.errStopApp, Arguments.Current.ApplicationName);
                Error(-203);
            }
            if (Arguments.Current.FullStop)
            {                                
                //In case of a full stop we also need to terminate instances
                Log.WriteInformation(Properties.Resources.msgTerminateInstances);
                try
                {
                    using (OperationsGroupProxy OperationsGroup = new OperationsGroupProxy(Arguments.Current.DatabaseServer, Arguments.Current.ManagementDatabase))
                    {
                        OperationsGroup.TerminateApplicationInstances(Arguments.Current.ApplicationName);
                    }
                }
                catch (Exception exTerminate)
                {
                    Log.WriteException(exTerminate);
                    Log.WriteEndError(Properties.Resources.errTerminatingInstances);
                    Error(-204);
                }
            }
            Log.UnIndent();
        }


        private void BackupApp()
        {
            if (Arguments.Current.BackupMSIFile != null)
            {
                Log.WriteStartInformation(Properties.Resources.msgBackup);
                try
                {
                    ICollection<Microsoft.BizTalk.ApplicationDeployment.Resource> res = new List<Microsoft.BizTalk.ApplicationDeployment.Resource>();
                    foreach (Microsoft.BizTalk.ApplicationDeployment.Resource resource in grpApp.ResourceCollection)
                    {
                        res.Add(resource);
                    }
                    grpApp.Export(Arguments.Current.BackupMSIFile, res);
                    Log.WriteEndSucess(Properties.Resources.msgMSIFileCreated, Arguments.Current.BackupMSIFile);
                }
                catch (Exception ex)
                {
                    Log.WriteException(ex);
                    Log.WriteEndError(Properties.Resources.errCreatingMSI);
                    Error(-202);
                }
            }
        }


        private void UnGAC()
        {
            if (Arguments.Current.UnGAC)
            {
                Log.WriteStartInformation(Properties.Resources.msgRemovingGAC);
                StringCollection AssembliesToRemoveFromGac = new StringCollection();
                ICollection<Microsoft.BizTalk.ApplicationDeployment.Resource> res = new List<Microsoft.BizTalk.ApplicationDeployment.Resource>();
                Log.Indent();
                Log.WriteInformation(Properties.Resources.msgListAsm);
                Log.Indent();
                foreach (Microsoft.BizTalk.ApplicationDeployment.Resource resource in grpApp.ResourceCollection)
                {
                    if ((resource.ResourceType == "System.BizTalk:BizTalkAssembly") || (resource.ResourceType == "System.BizTalk:Assembly"))
                    {
                        Log.WriteInformation(Properties.Resources.msgFoundAsm,resource.Luid);
                        try
                        {

                            Assembly asm = Assembly.Load(resource.Luid); //bug : works only when assembly is localy deployed
                            AssemblyName nom = asm.GetName();
                            string processorArchitectureName = Enum.GetName(typeof(ProcessorArchitecture), nom.ProcessorArchitecture);
                            AssembliesToRemoveFromGac.Add(nom + ",ProcessorArchitecture=" + processorArchitectureName);
                            Log.WriteSucess(Properties.Resources.msgProcArch,processorArchitectureName);
                        }
                        catch
                        {
                            Log.WriteInformation(Properties.Resources.errProcArch);
                            AssembliesToRemoveFromGac.Add(resource.Luid + ",ProcessorArchitecture=msil"); //default to msil
                        }
                    }
                }
                Log.UnIndent();
                Log.UnIndent();
                if (AssembliesToRemoveFromGac.Count == 0)
                {
                    Log.WriteInformation(Properties.Resources.msgNoAsm);
                }
                else
                {
                    StringCollection servers = null;
                    Log.WriteStartInformation(Properties.Resources.msgServerList);
                    try
                    {
                        servers = BiztalkWMIOperations.GetServers(Arguments.Current.DatabaseServer, Arguments.Current.ManagementDatabase);
                        Log.WriteEndSucess(Properties.Resources.msgCountServer,servers.Count);
                    }
                    catch (Exception exservers)
                    {
                        Log.WriteException(exservers);
                        Log.WriteEndError(Properties.Resources.errServerList);
                        Error(-205);
                    }
                                 
                    foreach (string server in servers)
                    {
                        Log.WriteStartInformation( Properties.Resources.msgRemoveGACServer, server);
                        try
                        {
                            Log.WriteStartInformation(Properties.Resources.msgUploading);
                            using (FileStream fs = new FileStream(string.Format(@"\\{0}\admin$\UG.exe", server), FileMode.Create, FileAccess.Write))
                            {
                                fs.Write(Properties.Resources.UG, 0, Properties.Resources.UG.Length);
                                fs.Close();
                            }
                            Log.WriteEndSucess(Properties.Resources.msgUploaded);
                        }
                        catch
                        {
                            Log.WriteEndError(Properties.Resources.errUpload, server);
                            continue;
                        }
                        ROOT.CIMV2.Process.StaticScope = new System.Management.ManagementScope(string.Format(@"\\{0}\ROOT\CIMV2", server));
                        foreach (string asmName in AssembliesToRemoveFromGac)
                        {
                            string smallAssemblyName = asmName.Split(',')[0];
                            Log.WriteStartInformation(Properties.Resources.msgRemoveAsm, smallAssemblyName);
                            uint ProcessId;
                            uint resu = ROOT.CIMV2.Process.Create(string.Format("UG \"{0}\"", asmName), null, null, out ProcessId);
                            if (resu == 0) //will have to check return code later. This only means that GU.exe was called
                            {
                                Log.WriteEndSucess(Properties.Resources.msgAsmRemoved, asmName.Split(',')[0], server);
                            }
                            else
                                Log.WriteEndError(Properties.Resources.errRemoveAsm, smallAssemblyName, server);
                            
                        }//end foreach assemblies
                    }//end foreach server
                    
                }//end of remove assemblies
                Log.UnIndent();
                Log.UnIndent();
            }//end if ungac
        }

        private void DeleteApp()
        {
            Log.WriteStartInformation(Properties.Resources.msgDeletingApp, Arguments.Current.ApplicationName);
            try
            {
                grp.Applications.Remove(grpApp);
                grp.Commit();
                Log.WriteEndSucess(Properties.Resources.msgAppDeleted, Arguments.Current.ApplicationName); //
            }
            catch (Exception ex)
            {
                Log.WriteException(ex);
                Log.WriteEndError(Properties.Resources.errDeletingApp, Arguments.Current.ApplicationName);
                Error(-203);
            }

        }

        private void RestartHostInstances()
        {
            bool someError = false;
            bool someSuccess = false;
            if (Arguments.Current.RestartHostInstances)
            {
                Log.WriteStartInformation(Properties.Resources.msgRestartingHost);
                try
                {
                    foreach (Microsoft.BizTalk.ExplorerOM.Host host in catalogExplorer.Hosts)
                    {
                        if (host.Type == Microsoft.BizTalk.ExplorerOM.HostType.InProcess)
                        {
                            Log.WriteStartInformation(Properties.Resources.msgRestartingHInstance, host.Name);
                            try
                            {
                                BiztalkWMIOperations.RestartHostInstances(Arguments.Current.DatabaseServer, Arguments.Current.ManagementDatabase);
                                Log.WriteEndSucess(Properties.Resources.msgHInstanceRestsarted, host.Name);
                                someSuccess = true;
                            }
                            catch (Exception exHost)
                            {
                                someError = true;
                                Log.WriteException(exHost);
                                Log.WriteEndError(Properties.Resources.errRestartHInstance, host.Name);
                            }
                        }
                    }
                    if (someSuccess && !someError)
                        Log.WriteEndSucess(Properties.Resources.msgHostRestarted);
                    else
                        if (someSuccess)
                            Log.WriteEndWarning(Properties.Resources.msgPartialHostRestart);
                        else
                            Log.WriteEndError(Properties.Resources.errRestartHost);
                }
                catch (Exception ex)
                {
                    Log.WriteException(ex);
                    Log.WriteEndError(Properties.Resources.errListHost);
                }
            }            

        }


        private void UnInstallMSI()
        {
            if (!string.IsNullOrEmpty(Arguments.Current.MSI))
            {
                //in case somebody think that he should give a path 
                //or in case somebody don't put the extension 
                Arguments.Current.MSI = System.IO.Path.GetFileNameWithoutExtension(Arguments.Current.MSI) + ".msi";

                Log.WriteStartInformation(Properties.Resources.msgStartUninstallMSI);
                StringCollection servers = null;
                Log.WriteStartInformation(Properties.Resources.msgServerList);
                try
                {
                    servers = BiztalkWMIOperations.GetServers(Arguments.Current.DatabaseServer, Arguments.Current.ManagementDatabase);
                    Log.WriteEndSucess(Properties.Resources.msgCountServer, servers.Count);
                }
                catch (Exception exservers)
                {
                    Log.WriteException(exservers);
                    Log.WriteEndError(Properties.Resources.errServerList);
                    Error(-205);
                }
                bool someSuccess = false;
                bool someError = false;
                foreach (string server in servers)
                {
                    Log.WriteStartInformation(Properties.Resources.msgStartUninstallMSIServer, server);
                    try
                    {
                        if (BTSZap.BiztalkWMIOperations.UninstallMSI(server, Arguments.Current.MSI))
                        {
                            Log.WriteEndSucess(Properties.Resources.msgUnisntallMSIServer, server);
                            someSuccess = true;
                        }
                        else
                        {
                            Log.WriteEndError(Properties.Resources.errUninstallMSIServer, server);
                            someError = true;
                        }
                    }
                    catch (Exception ex)
                    {
                        someError = true;
                        Log.WriteException(ex);
                        Log.WriteEndError(Properties.Resources.errUninstallMSIServer, server);
                    }
                }
                if (someSuccess && !someError)
                {
                    Log.WriteEndSucess(Properties.Resources.msgUninstallMSI);
                }
                else
                    if (someSuccess && someError)
                    {
                        Log.WriteEndWarning(Properties.Resources.msgPartialUnistallMSI);
                    }
                    else
                    {
                        Log.WriteEndError(Properties.Resources.errUninstallMSI);
                    }
                
            }
        }

        private void UnInstall()
        {
            
            Log.WriteInformation(Properties.Resources.msgStartUninstall , Arguments.Current.ApplicationName);
            if (Arguments.Current.AutoDetectConfig) AutoDetection();
            Break("Acces Group");
            AccessGroup();
            Break("Access App");
            AccessApp();
            Break("Uninstall");
            UnInstallMSI();
            Break("Stop App");
            StopApp();
            Break("Backup App");
            BackupApp();
            Break("UnGAC asm");
            UnGAC();
            Break("Delete App");
            DeleteApp();
            Break("Restart Host Instances");
            RestartHostInstances();           
        }

        static void Main(string[] args)
        {
            new Program().Run(args);   
        }
    }
}
